package measurement.recognition;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import measurement.motion.WiimoteMotionListener;
import wiiusej.Wiimote;
import wiiusej.apptest.activityTasks.GameModes;
import wiiusej.apptest.activityTasks.HeuristicStandAloneImpl;
import wiiusej.apptest.activityTasks.RealTimeFeedback;
import wiiusej.apptest.statistics.AccelerationPaquet;
import wiiusej.apptest.statistics.PacketController;
import wiiusej.motionAlgorithms.Centroid;
import wiiusej.motionAlgorithms.CentroidsEnum;
import wiiusej.motionAlgorithms.KmeansImpl;
import wiiusej.wiiusejevents.physicalevents.MotionSensingEvent;


public class WiimoteRecognitionListener extends WiimoteMotionListener{

	//Logger
	private static Logger loggy;
	private final static String recognitionLogger = "wiimoteRecognitionListener";
		
	//COM QUEUES
	private BlockingQueue<Vector<AccelerationPaquet>> toHeuristicAlgoritm 
		= new LinkedBlockingDeque<Vector<AccelerationPaquet>>();
	
	private BlockingQueue<HashMap<GameModes, Integer>> fromListenerResults 
	= new LinkedBlockingDeque<HashMap<GameModes, Integer>>();
	
	//Heuristic
	private HashMap<GameModes, Integer> heuristicResults = new HashMap<GameModes, Integer>();
	
	//Kmeans algorithm
	private KmeansImpl kmeansController;
	private CentroidsEnum centroidsType;
	protected Vector<Centroid> centroids = new Vector<Centroid>();
	
	
	//PacketController
	private PacketController pController;
	
	//file path
	private static SerializedFile file = new SerializedFile();
	
	private Timestamp lastPacketTime = null;
	
	public WiimoteRecognitionListener(ExecutorService exe, Wiimote aux,
			String measurementLength) {
		
		super(exe, aux, measurementLength);		
		
		PropertyConfigurator.configure("log4j.properties");
		loggy = Logger.getLogger(recognitionLogger);
		
		loggy.debug("******************************************");
		loggy.debug("INIT WiimoteRecognitionListener");
		loggy.debug("******************************************");
		
		loggy.debug("MeasurementLength: " + measurementLength);
		
		
		//Init Components
		initComponents();
	}
		
	
	private void initComponents(){
		initPacketController();
		initRTF();
	}
	
	private void initRTF() {		
		
		//heuristic algorithm
		Runnable heuristicRecognition = new HeuristicStandAloneImpl(toHeuristicAlgoritm,
				fromListenerResults);
		this.exe.execute(heuristicRecognition);	
		
		//k-means algorithm
		this.kmeansController = new KmeansImpl(wiimote.getId());
	}
	
	private void initPacketController(){
		long random = 123;		
		pController = new PacketController(wiimote.getId(), random, null);
	}
	
	
	@Override
	public void onMotionSensingEvent(MotionSensingEvent event, long packetRefNumber, Timestamp packetTime) {

		loggy.info("Incomming packet");
		
		loggy.debug("MyPacketsSize: " + myPackets.getMyEventVector().getPackets().size());
		
		//termination condition checkin - measurement length
		if(myPackets.getMyEventVector().getPackets().size() < measurementLength){	
					
			SimpleDateFormat tsFormat = new SimpleDateFormat("mm:ss:SS");
			//long start = System.currentTimeMillis();
			event.setRefNumber(packetRefNumber);
			event.setTime(packetTime);
			
			loggy.debug("Creating DataChunk");
			//Data chunk creation			
			createDataChunks(event, packetTime);
			
			//perform the heuristic motion recognition
			loggy.debug("Heuristic recognition");
			
			
			
			if(!myPackets.getMyEventVector().getPackets().isEmpty()){				
				
				if(lastPacketTime == null || myPackets.getMyEventVector().getPackets().lastElement().getTime().getTime() 
						!= lastPacketTime.getTime()){
					
					loggy.debug("PACKET ADDED!!!");
					
					//new packet added
					sendToHeuristic();				
					
					try {
						heuristicResults = fromListenerResults.take();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					//upload last packet time
					lastPacketTime = 
						new Timestamp(myPackets.getMyEventVector().getPackets().lastElement().getTime().getTime());
					
					loggy.info("PacketTime: " + tsFormat.format(lastPacketTime));	
					
				}				
			}			
			loggy.debug("");
			
		}else{			
						
			sendToKmeans();
			
			loggy.debug("PacketGathering Finished - Packets: " 
					+ myPackets.getMyEventVector().getPackets().size());
			//data acquisition has finished
			//serializeDataChunks();			
			
			//send chunks to both realtime feedback systems, heuristic and kmeans 
			//sendToRTF();			
			
			printResults();
			
			loggy.info("Removing Listener");
			//de-register listener from Wiimote
			removeListener();
			
		}
				
	}
	
	private void sendToKmeans() {
		
		loggy.debug("******************************************************");
		loggy.debug("");
		
		loggy.debug("K-Means Algorithm");
		
		//first we need to add 3 centroids to the centroid structure 
		initCentroids();
				
		//compute the motion recognition
		Vector<Centroid> centers = kmeansController.computeMotion((Vector<AccelerationPaquet>) 
				myPackets.getMyEventVector().getPackets().clone(),	
				(Vector<Centroid>) getCentroids().clone());
		
		
		setCentroids(centers);
		/*
		loggy.info("");
		loggy.info("MotionRecognition: "); 
		
		for(Centroid center : getCentroids()){
			loggy.info(center.getInitType() + ": " + center.getMotionSamples().size());			
		}*/
	}
	
	private void printResults(){
		
		loggy.info("**********************************************************");
		loggy.info("HEURISTIC RESULTS:");
		
		Iterator it = heuristicResults.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry e = (Map.Entry)it.next();
			loggy.info(e.getKey() + ": " + e.getValue());
		}		
		loggy.info("**********************************************************");
		
		
		loggy.info("**********************************************************");
		loggy.info("KMEANS RESULTS:");
		
		for(Centroid center : getCentroids()){
			loggy.info(center.getInitType() + ": " + center.getMotionSamples().size());			
		}
		
		loggy.info("**********************************************************");
	}
	
	private synchronized void setCentroids(Vector<Centroid> centroids) {
		this.centroids = (Vector<Centroid>) centroids.clone();
	}
	
	private synchronized Vector<Centroid> getCentroids() {
		return centroids;
	}
	
	
	
	
	private void initCentroids() {		
		Centroid slowCentroid = new Centroid(centroidsType.SLOW);
		Centroid bigCentroid = new Centroid(centroidsType.BIG);
		Centroid roboticCentroid = new Centroid(centroidsType.ROBOTIC);
		
		centroids.add(slowCentroid);
		centroids.add(bigCentroid);
		centroids.add(roboticCentroid);
		
		loggy.debug("");
		loggy.debug("Adding Centroids:");
		for(Centroid c : centroids){
			loggy.debug(c.toString());	
			loggy.debug(c.getMotionSamples());
		}
		
	}


	private void sendToHeuristic() {
		//first we call to the Heuristic Algorithm
		
		
		loggy.debug("Sending info to Heuristic");
		
		try {
			toHeuristicAlgoritm.put(myPackets.getMyEventVector().getPackets());
		} catch (InterruptedException e) {			
			e.printStackTrace();
		}		
	}
	
	private void createDataChunks(MotionSensingEvent event, Timestamp packetTime){
		this.myPackets = pController.makeChunks(event, myPackets, packetTime);		
	}
	

}
